import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:built_collection/built_collection.dart';
import 'package:dartz/dartz.dart';
import 'package:equatable/equatable.dart';
import 'package:http/http.dart' as http;
import 'package:website.core/core/domain/clients/api_client.dart';
import 'package:website.core/core/domain/config/config.dart';
import 'package:website.core/core/domain/entities/web/web_bank_card_info.dart';
import 'package:website.core/core/domain/entities/web/web_bank_card_type.dart';
import 'package:website.core/core/domain/entities/web/web_payment_method.dart';
import 'package:website.core/core/domain/entities/web/web_payment_method_failure.dart';
import 'package:website.core/core/domain/entities/web/yookassa/yookassa_bank_card_info.dart';
import 'package:website.core/core/domain/entities/web/yookassa/yookassa_payment_method.dart';
import 'package:website.core/core/domain/errors/exceptions.dart';
import 'package:website.core/core/domain/repositories/web_payment_method_repository/repository.dart';

part 'dtos/web_bank_card_info_dto.dart';

part 'dtos/web_bank_card_type_dto.dart';

part 'dtos/web_payment_method_dto.dart';

part 'dtos/web_payment_method_dto_factory.dart';

part 'dtos/web_payment_method_failure_dto.dart';

part 'dtos/web_saved_payment_method_getting_successful_response_dto.dart';

part 'dtos/yookassa/yookassa_bank_card_info_dto.dart';

part 'dtos/yookassa/yookassa_payment_method_dto.dart';

class WebPaymentMethodRepositoryImpl implements WebPaymentMethodRepository {
  const WebPaymentMethodRepositoryImpl({
    required ApiClient apiClient,
  }) : _apiClient = apiClient;

  final ApiClient _apiClient;

  @override
  Future<Either<WebSavedPaymentMethodGettingFailure, WebSavedPaymentMethodGettingResult>>
      getSavedPaymentMethods() async {
    final url = Uri.parse('${Config().apiBaseUrl}/web-saved-payment-methods');

    final http.Response response;

    try {
      response = await _apiClient.get(url);
    } on ServerException {
      const failure = ServerWebSavedPaymentMethodGettingFailure();
      return left(failure);
    } on SocketException {
      const failure = NetworkWebSavedPaymentMethodGettingFailure();
      return left(failure);
    } on TimeoutException {
      const failure = NetworkWebSavedPaymentMethodGettingFailure();
      return left(failure);
    }

    if (response.statusCode == 401) {
      const failure = UnauthorizedWebSavedPaymentMethodGettingFailure();
      return left(failure);
    }

    if (response.statusCode >= 500) {
      const failure = ServerWebSavedPaymentMethodGettingFailure();
      return left(failure);
    }

    if (response.statusCode != 200) {
      throw UnimplementedError();
    }

    final responseBody = jsonDecode(response.body) as Map<String, dynamic>;
    final successfulResponseDto =
        _WebSavedPaymentMethodGettingSuccessfulResponseDto.fromJson(responseBody);

    final savedPaymentMethods =
        successfulResponseDto.savedPaymentMethodDtos.map((paymentMethodDto) {
      return paymentMethodDto.toEntity();
    }).toBuiltList();

    final result = WebSavedPaymentMethodGettingResult(
      savedPaymentMethods: savedPaymentMethods,
    );

    return right(result);
  }

  @override
  Future<Either<WebSavedPaymentMethodRemovalFailure, void>> removeSavedPaymentMethod({
    required String paymentMethodId,
  }) async {
    final payload = {
      'web_payment_method_id': paymentMethodId,
    };

    final requestBody = jsonEncode(payload);

    final url = Uri.parse('${Config().apiBaseUrl}/remove-web-saved-payment-method');

    const headers = {
      'Content-Type': 'application/json',
    };

    final http.Response response;

    try {
      response = await _apiClient.post(
        url,
        body: requestBody,
        headers: headers,
      );
    } on ServerException {
      const failure = ServerWebSavedPaymentMethodRemovalFailure();
      return left(failure);
    } on SocketException {
      const failure = NetworkWebSavedPaymentMethodRemovalFailure();
      return left(failure);
    } on TimeoutException {
      const failure = NetworkWebSavedPaymentMethodRemovalFailure();
      return left(failure);
    }

    if (response.statusCode == 401) {
      const failure = UnauthorizedWebSavedPaymentMethodRemovalFailure();
      return left(failure);
    }

    if (response.statusCode >= 500) {
      const failure = ServerWebSavedPaymentMethodRemovalFailure();
      return left(failure);
    }

    if (response.statusCode != 200) {
      throw UnimplementedError();
    }

    return right(null);
  }
}
